package org.jradweb.core.web;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

/**
 * 
 * 
 * Created On 2008-6-25
 * 
 * @author <a href="mailto:wagzhi@gmail.com">Paul Wang</a>
 * @version $Id: DefaultUrlMapResolver.java 149 2008-10-10 03:10:10Z wagzhi $
 */
public class DefaultUrlMapResolver implements UrlMapResolver {
	private static final String DEFAULT_FILENAME_EXTENDSION = "html";
	private static final String DEFAULT_JS_FILENAME_EXTENDSION = "jjs";

	private String format=DEFAULT_FILENAME_EXTENDSION;
	private String jsFormat=DEFAULT_JS_FILENAME_EXTENDSION;
	private List<String> expressions;
	private List<Expression> expList = new ArrayList<Expression>();
	private Map<String, Map<String, String>> exps = new HashMap<String, Map<String, String>>();

	public List<String> getExpressions() {
		return expressions;
	}

	public void setExpressions(List<String> expressions) {
		this.expressions = expressions;
	}

	public DefaultUrlMapResolver() {
		this.format = DEFAULT_FILENAME_EXTENDSION;
	}

	public String getFormat() {
		return format;
	}

	public void setFormat(String format) {
		this.format = format;
	}

	private class Expression {
		private String expression;
		private Map<String, String> params;

		Expression(String expression, Map<String, String> params) {
			this.expression = expression;
			this.params = params;
		}

		public String getExpression() {
			return expression;
		}

		public void setExpression(String expression) {
			this.expression = expression;
		}

		public Map<String, String> getParams() {
			return params;
		}

		public void setParams(Map<String, String> params) {
			this.params = params;
		}
	}

	public void afterPropertiesSet() {
		// TODO add expression check
		for (String expression : expressions) {
			String[] values = StringUtils.split(expression, ",");
			String exp = values[0];
			Map<String, String> map = new HashMap<String, String>();
			for (int i = 1; i < values.length; i++) {
				String[] paramPair = StringUtils.split(values[i], "=");
				map.put(paramPair[0], paramPair[1]);
			}
			this.exps.put(exp, map);
			this.expList.add(new Expression(exp, map));
		}
	}

	public String getServletPath(Map<String, Object> params, int id) {
		String suffix = this.getFormat();
		switch (id) {
		case PAGE_TYPE_HTML:
			break;
		case PAGE_TYPE_JS:
			suffix = this.getJsFormat();
			break;
		default:
			;
		}
		return this.getServletPathWhithoutFormat(params) + "." + suffix;
	}

	public String getServletPath(Map<String, Object> params) {
		return this.getServletPathWhithoutFormat(params) + "." + this.getFormat();
	}

	public String getServletPathWhithoutFormat(Map<String, Object> params) {
		Map<String, String> strParams = new HashMap<String, String>();
		for (String key : params.keySet()) {
			Object value = params.get(key);
			// TODO use convert
			String strValue = value.toString();
			strParams.put(key, strValue);
		}
		for (Expression expression : this.expList) {
			if (this.match(expression, strParams)) {
				String urlPath = expression.expression;
				for (String key : params.keySet()) {
					String subExpression = this.getSubExpression(key);
					urlPath = urlPath
							.replace(subExpression, strParams.get(key));
				}
				return "/" + urlPath;
			}
		}
		return null;
	}
	public String getUri(String contextPath, Map<String, Object> params,int type) {
		String servletPath = this.getServletPath(params,type);
		return servletPath == null ? contextPath : contextPath + servletPath;
	}
	public String getUri(String contextPath, Map<String, Object> params) {
		String servletPath = this.getServletPath(params);
		return servletPath == null ? contextPath : contextPath + servletPath;
	}

	public Map<String, String> getParamters(String requestUri) {
		requestUri = requestUri.trim();

		// remove context path
		if (requestUri.startsWith("/")) {
			requestUri = requestUri.substring(1);
		}
		String servletPath = requestUri.substring(requestUri.indexOf("/"));

		int end = servletPath.indexOf(';');
		if (end == -1) {
			end = servletPath.indexOf('?');
			if (end == -1) {
				end = servletPath.length();
			}
		}

		// remove fomat
		servletPath = servletPath.substring(0, end);
		int dotIndex = servletPath.lastIndexOf('.');
		if (dotIndex != -1) {
			servletPath = servletPath.substring(0, dotIndex);
		}

		// urlPath is like /controller/action/name/id, no contextPath and format
		String exp = this.getExpression(servletPath);
		return this.getParamters(exp, servletPath);
	}

	public Map<String, String> getParamters(String exp, String urlPath) {
		String[] subRegexs = StringUtils.split(exp, "/");
		String[] pathNames = StringUtils.split(urlPath, "/");
		Map<String, String> params = new HashMap<String, String>();
		for (int i = 0; i < subRegexs.length; i++) {
			String subExpression = subRegexs[i];
			String subPath = pathNames[i];
			if (isVariable(subExpression)) {
				String key = getVariableName(subExpression);
				params.put(key, subPath);
			}
		}
		params.putAll(this.exps.get(exp));
		return params;
	}

	/**
	 * 根据路径获取获取匹配的表达式
	 * 
	 * @return
	 */
	public String getExpression(String urlPath) {
		for (Expression expression : this.expList) {
			if (this.match(expression.expression, urlPath)) {
				return expression.expression;
			}
		}

		return null;
	}

	/**
	 * 如果参数列表中的变量和表达式中的变量名称和数量完全匹配，则返回true
	 * 
	 * @param expression
	 * @param params
	 * @return
	 */
	public boolean match(String expression, Map<String, String> params) {
		String[] subExpressions = StringUtils.split(expression, "/");
		// 记录表达式中变量数目
		int count = 0;
		for (String subExpression : subExpressions) {
			if (this.isVariable(subExpression)) {
				String variableName = this.getVariableName(subExpression);
				if (!params.keySet().contains(variableName)) {
					return false;
				}
				count++;
			}
		}
		return count == params.size() ? true : false;
	}

	/**
	 * 如果参数列表中的变量和表达式中的变量名称和数量完全匹配，则返回true
	 * 
	 * @param expression
	 * @param params
	 * @return
	 */
	protected boolean match(Expression exp, Map<String, String> params) {
		String[] subExpressions = StringUtils.split(exp.expression, "/");
		List<String> varExp = this.getVarExps(subExpressions);

		if ((varExp.size() + exp.params.size()) != params.size()) {
			return false;
		}
		for (String varName : varExp) {
			if (!params.keySet().contains(varName)) {
				return false;
			}
		}
		for (String varName : exp.params.keySet()) {
			String value = exp.params.get(varName);
			Object paramValue = params.get(varName);
			if (!value.equals(paramValue)) {
				return false;
			}
		}
		return true;
	}

	private List<String> getVarExps(String[] subExpressions) {
		List<String> varExps = new ArrayList<String>();
		for (String subExpression : subExpressions) {
			if (this.isVariable(subExpression)) {
				String variableName = this.getVariableName(subExpression);
				varExps.add(variableName);
			}
		}
		return varExps;
	}

	/**
	 * 检查路径和表达式是否匹配
	 * 
	 * @param expression
	 * @param urlPath
	 * @return
	 */
	public boolean match(String expression, String urlPath) {
		String[] subExpressions = StringUtils.split(expression, "/");
		String[] pathNames = StringUtils.split(urlPath, "/");
		if (pathNames.length < subExpressions.length) {
			return false;
		}
		for (int i = 0; i < subExpressions.length; i++) {
			if (!matchSubPath(subExpressions[i], pathNames[i])) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 检查表达式和路径是否match：</br><li/>如表达式为变量，以${_controller_}则返回true</br>
	 * <li/>如表达式和路线相同（忽略大小写），则返回true</br>
	 * 
	 * 否则返回fasle
	 * 
	 * @param regex
	 * @param path
	 * @return
	 */
	private boolean matchSubPath(String subExpression, String subPath) {
		// TODO replace this with regex
		if (isVariable(subExpression)) {
			return true;
		} else if (subExpression.equals(subPath)) {
			return true;
		} else {
			return false;
		}
	}

	private String getSubExpression(String variableName) {
		return "$" + variableName;
	}

	private String getVariableName(String subExpression) {
		return subExpression.substring(1);
	}

	private boolean isVariable(String subExpression) {
		if (subExpression.startsWith("$")) {
			return true;
		}
		return false;
	}

	protected Map<String, Map<String, String>> getExps() {
		return exps;
	}

	protected void setExps(Map<String, Map<String, String>> exps) {
		this.exps = exps;
	}

	public String getJsFormat() {
		return jsFormat;
	}

	public void setJsFormat(String jsFormat) {
		this.jsFormat = jsFormat;
	}
}
